Lab 1: Artemis Setup and Communication

8 minutes read

Lab 1a

During section 1a of the lab I installed the Arduino IDE and established a wired connection to communicate with the Artemis Nano. To connect I had to select the correct board and port in the Arduino IDE. Then, to test the connection and explore the Arduino environment I completed the following assigned example sketches in the Arduino IDE:

You can see the Artemis board flash a bright blue led

Blink test video

Serial

Here we can see the Artemis recieves the string and echos it back Alt text

Serial output test

analogRead Temperature Sensor

Temperature sensor test

Microphone Output

From the video you can see the Artemis microphone successfully picking up the difference in sounds in the serial monitor

Micrphone output test

Lab 1b

Codebase and BLE

Bluetooth (specifically Bluetooth LE) at a high level is used to establish a connection between my computer and the Artemis:

The codebase is collection of source code files that make up our system. Some important components:

Configurations and Setup

  1. I started with installing venv: python3 -m pip install --user virtualenv

  2. I created the “FastRobots_ble” virtual environment inside my project directory: python3 -m venv FastRobots_ble

  3. I activated the virtual environment: source FastRobots_ble/bin/activate

  4. I downloaded the provided ble_robot_1.2 codebase into my project directory

  5. It is now time to start the Jupyter server: jupyter lab

  6. Updated the Artemis MAC Address on the Computer. Run the ble_arduino.ino file in the Arduino IDE and check the serial monitor the MAC address:

Alt text
MAC Address
  1. Generate new UUID: run from uuid import uuid4 and uuid4(). Input the generated UUID into the #define BLE_UUID_TEST_SERVICE line in ble_arduino.ino and into the ble_service: line in connections.yaml
Alt text
connections.yaml
Alt text
ble_arduino.ino
  1. Connect to the Artemis Nano via BLE
# Get ArtemisBLEController object
ble = get_ble_controller()

# Connect to the Artemis Device
ble.connect()
Alt text
Successful BLE Connection

Task 1

I sent a string value from my computer to the Artemis board using the ECHO command and the computer recieved and printed the augmented string

Arduino Code:

case ECHO:
    char char_arr[MAX_MSG_SIZE];

    // Extract the next value from the command string as a character array
    success = robot_cmd.get_next_value(char_arr);
    if (!success)
        return;

    tx_estring_value.clear();
    tx_estring_value.append("Robot is saying: ");
    tx_estring_value.append(char_arr);
    tx_estring_value.append("!!");
    
    
    tx_characteristic_string.writeValue(tx_estring_value.c_str());

    Serial.print("Sent back: ");
    Serial.println(tx_estring_value.c_str());
    
    break;

Jupyter Lab Code:

ble.send_command(CMD.ECHO, "Hi friend")
l = ble.receive_string(ble.uuid['RX_STRING'])
print(l)
Alt text
ECHO Output

Task 2

I sent three floats to the Artemis board using the SEND_THREE_FLOATS command and extracted the three floats in the Arduino sketch

Arduino Code:

case SEND_THREE_FLOATS:
            
    float float_a, float_b, float_c;

    // Extract the next value from the command string as an integer
    success = robot_cmd.get_next_value(float_a);
    if (!success)
        return;

    // Extract the next value from the command string as an integer
    success = robot_cmd.get_next_value(float_b);
    if (!success)
        return;

    success = robot_cmd.get_next_value(float_c);
    if (!success)
        return;   

    Serial.print("Three Floats: ");
    Serial.print(float_a);
    Serial.print(", ");
    Serial.println(float_b);
    Serial.print(", ");
    Serial.println(float_c);

    break;

Jupyter Lab Code:

ble.send_command(CMD.SEND_THREE_FLOATS, "3.14|2.22|8.89")
Alt text
SEND_THREE_FLOATS Output

Task 3

I added a GET_TIME_MILLIS which makes the robot reply write a string to the string characteristic. GET_TIME_MILLIS had to be added to the cmd_types.py file. GET_TIME_MILLIS had to be added to cmd_types.py to run. Note that the output looks the same as in task 4.

case GET_TIME_MILLIS:
    unsigned long elapsedT = 1;
    
    elapsedT = millis();
    tx_estring_value.clear();
    tx_estring_value.append("T: ");
    String strTime = String(elapsedT);
    tx_estring_value.append(strTime.c_str());
    tx_characteristic_string.writeValue(tx_estring_value.c_str());
        
        break;

Task 4

I setup a notification_handler function to receive the string value from the Artemis board and, in the callback function, extract the time from the string.

def notification_handler(bleuuid, byteArray):
    x = ble.bytearray_to_string(byteArray)
    print(x)
ble.start_notify(ble.uuid['RX_STRING'], notification_handler)
print("Started notifications on RX_STRING")
ble.send_command(CMD.GET_TIME_MILLIS, "")
Alt text
Notification Handler Output

Task 5

I made a twenty-five step loop that gets the current time in milliseconds using the GET_TIME_MILLIS function to then be processed by notification_handler(). From my output shown below you can see that there was an average 33.5 ms gap between the prints. This translates to 29.85 message transmissions per second. With each message being 9 bytes (1 char per each string sent), this results an effective data transfer rate of 269 bytes per second for this method.

x = 0
while x < 25:
    k = ble.send_command(CMD.GET_TIME_MILLIS, "")
    x+=1
Alt text
GET_TIME_MILLIS Loop Output

Task 6

I created a command SEND_TIME_DATA that loops though to add generated time steps via the millis() function and then stores them in an array. Then, in SEND_TIME_DATA I loop through the array and send each data point as a string to my laptop to be processed. SEND_TIME_DATA had to be added to the cmd_types.py file. Note that millisArray[i] is defined as a global array.

Arduino Code

case SEND_TIME_DATA: {

    int Millis_Cur = 0;
    
    if (!success) {
        return;
    }
    
    for (int i = 0; i < 24; i++){ 
    Millis_Cur = millis();
    millisArray[i] = Millis_Cur;
    }

    for (int x = 0; x < 24; x++){ 
    tx_estring_value.clear();
    tx_estring_value.append("T: ");
    tx_estring_value.append(millisArray[x]);
    tx_characteristic_string.writeValue(tx_estring_value.c_str());
    }

break;
} 

Jupyter Code:

ble.send_command(CMD.SEND_TIME_DATA, "")
Alt text
SEND_TIME_DATA Output

Task 7

I created a second array to store fahrenheit temperature readings with the same length as the one used in task 6. Each index in both global arrays (millisArray[] and tempArray[]) correspond to each other. The command GET_TEMP_READINGS loops through both arrays concurrently and sends each temperature reading with a time stamp. The notification handler parses these strings and populates the data into two lists. Note that GET_TEMP_READINGS had to be added to the cmd_types.py file.

case GET_TEMP_READINGS: {
            
    int Millis_Cur2 = 0;
    float temp_f_cur = 0;
    
    if (!success) {
        return;
    }
    
    for (int i = 0; i < 24; i++){ 
    Millis_Cur2 = millis();
    float temp_f_cur = getTempDegF();
    millisArray[i] = Millis_Cur2;
    tempArray[i] = temp_f_cur;
    }
    
    for (int x = 0; x < 24; x++){ 
    tx_estring_value.clear();
    tx_estring_value.append("T: ");
    tx_estring_value.append(millisArray[x]);
    tx_estring_value.append(" , ");
    tx_estring_value.append("F: ");
    tx_estring_value.append(tempArray[x]);
    tx_characteristic_string.writeValue(tx_estring_value.c_str());
    }
    
break; 
}
Alt text
Temp and Time Output

Task 8

Rate wise, it is clear from the time steps in tasks 5 vs. 6 that method one is considerably slower at recording data than method two. This is because method one has to wait until the Artemis sends data to the computer after every round of collection before recording again. Instead, the second method can effectively record data as fast as its slowest sensor, thus producing data that may be more accurate but at the expense of a delayed reception on the client’s end. This could result in a slower response time from the robot and thus is less applicable if the robot needs to make time-sensitive decisions from sensor data. For an open-loop test where we do not care as much about real-time feedback, method two may be more useful as the faster data recording would provide higher resolution.

In order to determine how quickly the second method records data, I had to increase the number of loop iterations to 100 in order to see a difference in time steps. The first element is T: 104510ms and the 100th is T: 104512ms which translates to data being recorded every 0.02ms on average (considerably faster than the 33.5 ms gap in task 5).

The millis() int variable and getTempDegF() int variable are both stored as ints of 4 bytes each for a total of 8 bytes. As printed by the Arduino IDE output, global variables use 30648 bytes. If the Artemis board has 384 kB of RAM, then 353,352 bytes remain allowing us to store a total of 353,352 bytes/8 bytes = 44,169 data points without running out of memory.

Discussion and Conclusion (Lab 1A & 1B)

  1. Learned about what functions are responsible for communication between my computer and Artemis and how the commands (ECHO, GET_TIME_MILLIS, etc.) are passed in via RobotCommand.h
  2. At first I was confused about the relationship between different data types and their byte size. However, the later questions in the lab made it clear how ints vs. strings require different number of bytes as well as how Estring char are used to send those types to the computer
  3. The largest problem I faced was understanding the parameters needed for the notification handler!

Collaboration

I collaborated extensively on this project with Jack Long and Trevor Dales. I referenced Daria’s site for code debugging and specific help with SEND_TIME_DATA and GET_TEMP_READINGS. ChatGPT was used for Lab 1B code debugging and website formatting/development.